﻿#include "json.h"
#include <QDebug>
#include <QFile>
#include <QRegularExpression>
#include <cctype>
#include <ranges>
#define USE_EXCEPTION _DEBUG
#ifdef _MSC_VER
#pragma warning(disable:4715)
#pragma warning(disable:4267)
#endif
namespace flow
{
class Json::Parser
{
public:
    explicit Parser(const String& str, std::function<void(int)> parseProgressChanged = {})
        : _str{str}
        , _cursor{0}
        , _parseProgressChanged(std::move(parseProgressChanged))
    { }

    ~Parser() = default;

    Json parse()
    {
        const int progress = float(_cursor + 1) / float(_str.length()) * 1000;
        if (progress != _progress) {
            _progress = progress;
            if (_parseProgressChanged) {
                _parseProgressChanged(_progress);
            }
            // qDebug() << "json parse progress: " << progress / 10;
        }
        const auto ch = getNextToken();
        if ('n' == ch) {
            --_cursor;
            return getNull();
        }
        if (ch == 't' || ch == 'f') {
            --_cursor;
            return getBool();
        }
        if ('-' == ch || ch.isDigit()) {
            --_cursor;
            return getNumber();
        }
        if ('[' == ch) {
            return getArray();
        }
        if ('{' == ch) {
            return getObject();
        }
        if ('"' == ch) {
            return getString();
        }
        throw std::logic_error(("unexpected token: " + QString(ch) + "arround: " + QString::number(_cursor)).toStdString());
    }

private:
    void skipWhiteSpace()
    {
        if (_cursor < _str.length()) {
            auto ch = _str.at(_cursor);
            while (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t') {
                _cursor++;
                ch = _str.at(_cursor);
            }
        }
    }

    QChar getNextToken()
    {
        skipWhiteSpace();
        if (_cursor == _str.size()) {
            throw std::logic_error("check format");
        }
        return _str.at(_cursor++); // std::string [_cursor++] ;
    }

    Json getNull()
    {
        // if (_str == "null") {
        if (_str.mid(_cursor, 4) == "null") {
            // if (_str.compare(_cursor, 4, "null") == 0) {
            _cursor += 4;
            return {};
        }
        throw std::logic_error("parse null error");
    }

    Json getBool()
    {
        if (_str.mid(_cursor, 4) == "true") {
            _cursor += 4;
            return true;
        }
        if (_str.mid(_cursor, 5) == "false") {
            _cursor += 5;
            return false;
        }
        throw std::logic_error(("expected 'true' or 'false' given: " + _str.mid(_cursor, 4)).toStdString());
    }

    Json getNumber()
    {
        const auto start = _cursor;

        if (_str[_cursor] == '-') {
            _cursor++;
        }

        // integer part
        if (_str[_cursor] == '0') {
            _cursor++;
        }
        else if (_str[_cursor] >= '1' && _str[_cursor] <= '9') {
            _cursor++;
            while (_str[_cursor] >= '0' && _str[_cursor] <= '9') {
                _cursor++;
            }
        }
        else {
            throw std::logic_error("invalid character in number");
        }
        const bool hasExp = (_str[_cursor] == 'e' || _str[_cursor] == 'E');
        if (_str[_cursor] != '.' && !hasExp) {
            // return std::strtol(_str.c_str() + start, nullptr, 10);
            const auto numStr = _str.mid(start, _cursor - start); // 提取字符串
            bool ok;
            numStr.toLongLong(&ok);       // 尝试转化为int
            if (!ok) {                    // 如果转化失败，说明数值超过int的处理范围
                return numStr.toDouble(); // 转换为double类型
            }
            return numStr.toDouble(); // 否则，它还在int的范围内，所以我们使用toInt()函数将其转化为int
            // return std::stoi(_str.substr(start, _cursor - start));
        }
        // decimal part
        if (!hasExp) {
            _cursor++;
            while (_str[_cursor].isDigit()) {
                _cursor++;
            }
        }
        parseExponentPart();
        return _str.mid(start, _cursor - start).toDouble();
        // return std::strtod(_str.c_str() + start, nullptr);
    }

    void parseExponentPart()
    {
        if (_str[_cursor] != 'e' && _str[_cursor] != 'E') {
            return;
        }

        _cursor++;
        // Check for an optional sign.
        if (_str[_cursor] == '+' || _str[_cursor] == '-') {
            _cursor++;
        }

        // We must have a digit. Otherwise, we have an error.
        if (!_str[_cursor].isDigit()) {
            throw std::logic_error("at least one digit required in fractional part");
        }
        while (_str[_cursor].isDigit()) {
            _cursor++;
        }
    }

    String getString()
    {
        const auto start = _cursor;
        while (true) {
            if (_cursor == _str.size()) {
                throw std::logic_error("unexpected end of input in string");
            }

            auto ch = _str[_cursor++];
            if (ch == '"') {
                break;
            }

            // The usual case: non-escaped characters
            if (ch == '\\') {
                ch = _str[_cursor++];
                switch (ch.toLatin1()) {
                case 'b':
                case 't':
                case 'n':
                case 'f':
                case 'r':
                case '"':
                case '\\': break;
                case 'u': _cursor += 4; break;
                default: break;
                }
            }
        }
        return _str.mid(start, _cursor - start - 1);
    }

    Json getArray()
    {
        Json arr(Type::Array);
        auto ch = getNextToken();
        if (ch == ']') {
            return arr;
        }
        _cursor--;
        while (true) {
            arr.push_back(parse());
            ch = getNextToken();
            if (ch == ']') {
                break;
            }
            if (ch != ',') {
                throw std::logic_error("expected ',' in array");
            }
        }
        return arr;
    }

    Json getObject()
    {
        Json obj(Type::Object);
        auto ch = getNextToken();
        if (ch == '}') {
            return obj;
        }
        _cursor--;
        while (true) {
            ch = getNextToken();
            if (ch != '"') {
                throw std::logic_error("expected '\"' in object");
            }
            String key = getString();
            ch         = getNextToken();
            if (ch != ':') {
                throw std::logic_error("expected ':' in object");
            }
            obj[key] = parse();
            ch       = getNextToken();
            if (ch == '}') {
                break;
            }
            if (ch != ',') {
                throw std::logic_error("expected ',' in object");
            }
        }
        return obj;
    }

    // match
    // peek            获取游标处字符
    // peekNext        下一个字符
    // advance()       获取游标处字符，且游标后移
    // previous        游标前一个字符
    // match(...)      这个检查会判断当前的标记是否属于给定的类型之一
private:
    const String& _str;
    int _cursor;
    int _progress = 0;
    std::function<void(int)> _parseProgressChanged;
};

Json::Json(Type t)
    : type_(t)
{
    switch (t) {
    case Type::Null: break;
    case Type::Bool: {
        *this = Json(t, false);
        // 原地构造
        // this->type_ = t;
        // this->data_ = std::make_unique<Data>(false);
    } break;
    case Type::Int: {
        *this = Json(t, 0ll);
    } break;
    case Type::Double: {
        *this = Json(t, .0);
    } break;
    case Type::String: {
        *this = Json(t, "");
    } break;
    case Type::Array: {
        *this = Json(t, Array());
    } break;
    case Type::Object: {
        *this = Json(t, Object());
    } break;
    }
}

Json::Json(Type t, const Data& d)
    : type_(t)
    , data_(d)
{
    // type != d.type()?
    // data_ = std::make_unique<Data>(d);
}

Json::Json(const QStringList& stringList)
{
    *this = Json::array();
    for (const auto& str : stringList) {
        push_back(str);
    }
}

Json::Json(const Json& json)
{
    // 调用右值拷贝构造, swap(*this, Json(json));
    *this = json;
}

Json Json::parse(const char* str, int* errorCode, std::function<void(int)> progressCallback)
{
    return Parser(str, progressCallback).parse();
}

Json Json::parseFile(const QString& filename, int* errorCode, std::function<void(int)> progressCallback)
{
    try {
        QFile file(filename);
        if (!file.open(QIODevice::ReadOnly)) {
            return {};
        }
        const auto data = file.readAll();
        file.close();
        return parse(data, errorCode, progressCallback);
    }
    catch (...) {
    }
    return {};
}

bool Json::saveToFile(const QString& filename, int* errorCode) const
{
    try {
        QFile file(filename);
        if (!file.open(QIODevice::WriteOnly)) {
            return false;
        }
        file.write(dump().toUtf8());
        file.close();
        return true;
    }
    catch (...) {
    }
    return false;
}

Json Json::parse(const QByteArray& str, int* errorCode, std::function<void(int)> progressCallback)
{
    return Parser(QString::fromUtf8(str), progressCallback).parse();
}

Json Json::parse(const String& str, int* errorCode, std::function<void(int)> progressCallback)
{
#if USE_EXCEPTION
    try {
#endif
        return Parser(str, progressCallback).parse();
#if USE_EXCEPTION
    }
    catch (std::exception& e) {
        qDebug() << "json parse error" << e.what();
    }
    catch (...) {
    }
#endif
    return {};
}

Json::String Json::dump(bool compact) const
{
#if USE_EXCEPTION
    try {
#endif
        switch (type_) {
        case Type::Null: {
            return "null";
        }
        case Type::Bool: {
            if (toBool()) {
                return "true";
            }
            return "false";
        }
        case Type::Int: {
            // return String::number(toInt());
            return std::to_string(toInt()).c_str();
        }
        case Type::Double: {
            // return String::number(toDouble());
            return std::to_string(toDouble()).c_str();
        }
        case Type::String: {
            return "\"" + toString() + "\"";
        }
        case Type::Array: {
            const auto d = std::get_if<Array>(&data_);
            if (!d) {
                return {};
            }
            String str = "[";
            for (int i = 0; i < d->size(); ++i) {
                str += d->at(i).dump() + ((i == d->size() - 1) ? "]" : ",");
            }
            return str;
        }
        case Type::Object: {
            const auto d = std::get_if<Object>(&data_);
            if (!d) {
                return {};
            }
            String str = "{";
            for (auto it = d->begin(); it != d->end(); ++it) {
                // const auto& [key, value] = *it;
                const auto key   = it.key();
                const auto value = it.value();
                str += (it == d->begin() ? "" : ",") + ("\"" + key + "\":" + value.dump());
            }
            str += "}";
            return str;
        }
        }
#if USE_EXCEPTION
    }
    catch (std::exception& e) {
        qDebug() << "json parse error" << e.what();
    }
    catch (...) {
    }
#endif
    return {};
}

bool Json::toBool(const bool defaultValue) const
{
    if (isNull()) {
        return false;
    }
    if (const auto d = std::get_if<bool>(&data_)) {
        return *d;
    }
    return defaultValue;
}

Json::Int Json::toInt(const Int defaultValue) const
{
    if (isDouble()) {
        return static_cast<Int>(toDouble());
    }
    else if (const auto d = std::get_if<Int>(&data_)) {
        return *d;
    }
    else if (isString()) {
        return toString().toInt();
    }
    return defaultValue;
}

double Json::toDouble(const double defaultValue) const
{
    if (isInt()) {
        return static_cast<double>(toInt());
    }
    if (const auto d = std::get_if<double>(&data_)) {
        return *d;
    }
    if (isString()) {
        return toString().toDouble();
    }
    return defaultValue;
}

Json::String Json::toString(const String& defaultValue) const
{
    if (const auto d = std::get_if<String>(&data_)) {
        return *d;
    }
    if (isBool()) {
        return toBool() ? "true" : "false";
    }
    if (isInt()) {
        return String::number(toInt());
    }
    if (isDouble()) {
        return String::number(toDouble());
    }
    return defaultValue;
}

std::string Json::toStdString(const QString& defaultValue) const
{
    return toString(defaultValue).toStdString();
}

Json::Array Json::toArray(const Array& defaultValue) const
{
    if (const auto d = std::get_if<Array>(&data_)) {
        return *d;
    }
    return defaultValue;
}

Json::Object Json::toObject(const Object& defaultValue) const
{
    if (const auto d = std::get_if<Object>(&data_)) {
        return *d;
    }
    return defaultValue;
}

bool Json::operator==(const Json& other) const
{
    return (type_ == other.type_ && this->data_ == other.data_);
}

bool Json::operator==(const qsizetype other) const
{
    if (!isInt() or !isDouble()) {
        return false;
    }
    return this->toInt() == other;
}
bool Json::operator==(const int other) const
{
    if (!isInt() or !isDouble()) {
        return false;
    }
    return toInt() == other;
}
bool Json::operator==(const double other) const
{
    if (!isInt() or !isDouble()) {
        return false;
    }
    return toDouble() == other;
}

bool Json::operator==(const QString& other) const
{
    return (isString() && this->toString() == other);
}

bool Json::operator==(const QChar& other) const
{
    return (isString() && this->toString() == other);
}

bool Json::operator!=(const Json& other) const
{
    return !(*this == other);
}

Json& Json::operator/=(double value)
{
    if (isDouble()) {
        *this = toDouble() / value;
    }
    return *this;
}

bool Json::push_back(const Json& other)
{
    if (Type::Null == type_) {
        *this = Json::array();
    }
    if (type_ == Type::Array) {
        if (const auto d = std::get_if<Array>(&data_)) {
            d->push_back(other);
            return true;
        }
    }
    return false;
}

bool Json::push_back(Json&& other)
{
    if (Type::Null == type_) {
        *this = Json(Type::Array);
    }
    if (type_ == Type::Array) {
        if (const auto d = std::get_if<Array>(&data_)) {
            d->push_back(std::move(other));
            return true;
        }
    }
    return false;
}

bool Json::remove(const String& key)
{
    if (type_ == Type::Object) {
        if (const auto d = std::get_if<Object>(&data_)) {
            // d->erase(key);
            d->remove(key);
            return true;
        }
    }
    return false;
}

bool Json::remove(const int i)
{
    if (type_ == Type::Array) {
        if (const auto d = std::get_if<Array>(&data_)) {
            d->erase(d->begin() + i);
            return true;
        }
    }
    return false;
}

void Json::clear()
{
    if (isArray()) {
        if (const auto d = std::get_if<Array>(&data_)) {
            Array().swap(*d);
        }
    }
    if (isObject()) {
        if (const auto d = std::get_if<Object>(&data_)) {
            Object().swap(*d);
        }
    }
    *this = Json();
}

Json& Json::operator[](const String& key)
{
    if (type_ == Type::Null) {
        *this = Json::object();
    }
    const auto d = std::get_if<Object>(&data_);
    return (*d)[key];
}

Json& Json::operator[](const std::string_view& key)
{
    return this->operator[](key.data());
}

Json& Json::operator[](const char* key)
{
    return this->operator[](String(key));
}

Json& Json::operator[](const int i)
{
    if (type_ == Type::Null) {
        *this = Json::array();
    }
    const auto d = std::get_if<Array>(&data_);
    if (i < 0) {
        throw std::runtime_error("index out of range !");
    }
    if (i >= d->size()) {
        d->resize(i + 1);
    }
    return (*d)[i];
}

Json Json::operator[](const QString& key) const
{
    return value(key.toStdString());
}

Json Json::operator[](const char* key) const
{
    return value(key);
}

Json Json::at(const int i) const
{
    const auto d = std::get_if<Array>(&data_);
    if (!d) {
        return throwError("not an array !");
    }

    if (i < 0 || i > d->size()) {
        // throw std::runtime_error("index out of range !");
        return throwError("index out of range !");
    }
    return d->at(i);
}

Json::operator bool() const
{
    return toBool();
}

Json::operator double() const
{
    return toDouble();
}

Json::operator Int() const
{
    return toInt();
}

Json::operator int() const
{
    return static_cast<int>(toInt());
}

Json::operator String() const
{
    return toString();
}

// Json::operator QByteArray() const
//{
//     return toString().toLocal8Bit();
// }

Json::operator std::string() const
{
    return toString().toStdString();
}

Json::iterator Json::begin() noexcept
{
    if (type() == Type::Array) {
        if (const auto b = std::get_if<Array>(&data_)) {
            return b->begin();
        }
    }
    if (type() == Type::Object) {
        if (const auto b = std::get_if<Object>(&data_)) {
            return b->begin();
        }
    }
    throwError("The type does not support iterator");
    Object emptyObj; // 静态的空对象
    return emptyObj.begin();
}

Json::iterator Json::end() noexcept
{
    if (type() == Type::Array) {
        if (const auto b = std::get_if<Array>(&data_)) {
            return b->end();
        }
    }
    if (type() == Type::Object) {
        if (const auto b = std::get_if<Object>(&data_)) {
            return b->end();
        }
    }
    // return iterator();
    throwError("The type does not support iterator");
    Object emptyObj; // 静态的空对象
    return emptyObj.end();
}

Json::const_iterator Json::const_begin() const noexcept
{
    if (type() == Type::Array) {
        if (const auto b = std::get_if<Array>(&data_)) {
            return b->constBegin();
        }
    }
    if (type() == Type::Object) {
        if (const auto b = std::get_if<Object>(&data_)) {
            return b->constBegin();
        }
    }
    throwError("The type does not support iterator");
    Object const emptyObj; // 静态的空对象
    return emptyObj.constEnd();
}

Json::const_iterator Json::const_end() const noexcept
{
    if (type() == Type::Array) {
        if (const auto b = std::get_if<Array>(&data_)) {
            return b->constEnd();
        }
    }
    if (type() == Type::Object) {
        if (const auto b = std::get_if<Object>(&data_)) {
            return b->constEnd();
        }
    }
    throwError("The not support iterator");
    Object const emptyObj; // 静态的空对象
    return emptyObj.constEnd();
}

void Json::setParseProgressChanged(const std::function<void(int)>& callback)
{
    _parseProgressChanged = callback;
}

Json Json::map(const std::function<Json(const Json& v)>& f) const
{
    if (isArray()) {
        if (const auto d = std::get_if<Array>(&data_)) {
            // return *d | std::ranges::views::transform(f) | std::ranges::to<Array>();
            Array result;
            result.reserve(d->size());
            std::ranges::transform(*d, std::back_inserter(result), f);
            return result;
        }
    }
    return {Type::Null};
}
Json Json::filter(const std::function<Json(const Json& v)>& f) const
{
    if (isArray()) {
        if (const auto d = std::get_if<Array>(&data_)) {
            // return *d | std::ranges::views::transform(f) | std::ranges::to<Array>();
            Array result;
            result.reserve(d->size());
            std::ranges::copy_if(*d, std::back_inserter(result), f);
            return result;
        }
    }
    return {Type::Null};
}

void Json::reserve(qsizetype s)
{
    if (const auto d = std::get_if<Array>(&data_)) {
        d->reserve(s);
    }
}

Json& Json::insert(const QString& key, const Json& value)
{
    if (type_ == Type::Null) {
        *this = Json::object();
    }
    if (type_ == Type::Object) {
        if (const auto d = std::get_if<Object>(&data_)) {
            (*d)[key] = value;
            return *this;
        }
    }
    return *this;
}

Json Json::throwError(const std::string_view& message) const
{
#if USE_EXCEPTION
    try {
#endif
        throw std::logic_error(message.data());
#if USE_EXCEPTION
    }
    catch (std::exception& e) {
        qDebug() << "json exception: " << e.what();
    }
    catch (...) {
    }
#endif
    return {};
}
} // namespace flow
